// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DepthIRColorCameraModelAgent.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace Microsoft.Robotics.Vision.Visualization
{
    using System;
    using System.Runtime.Serialization;
    using Microsoft.Robotics.Runtime;
    using Microsoft.Robotics.Vision.Cameras;
    using Microsoft.Robotics.Vision.Runtime.Cameras;
    using Microsoft.Robotics.Visualization;

    /// <summary>
    /// This model agent class prepares the depth frame for visualization.
    /// </summary>
    /// <typeparam name="TDepthParameters">The parameters for the depth camera</typeparam>
    /// <typeparam name="TIRParameters">The parameters for the IR camera</typeparam>
    /// <typeparam name="TColorParameters">The parameters for the color camera</typeparam>
    [DataContract]
    public sealed class DepthIRColorCameraModelAgent<TDepthParameters, TIRParameters, TColorParameters> : ModelAgent<DepthIRColorCameraMessage<TDepthParameters, TIRParameters, TColorParameters>, DepthCameraModel>
        where TDepthParameters : DepthParameters
        where TIRParameters : IRParameters
        where TColorParameters : ColorParameters
    {
        /// <summary>
        /// Factor used to accentuate the difference between nearby pixels
        /// </summary>
        [DataMember(Name = "DisparityFactor")]
        private int disparityFactor;

        /// <summary>
        /// Select the type of display: The default (Gray16) is regular gray scale. 
        /// The alternate is also a gray scale but marks special pixels as:
        /// Red -  No reading
        /// Green - Further than maximum
        /// Blue - Less than minimum
        /// </summary>
        [DataMember(Name = "ImageFormat")]
        private SimplePixelFormat imageFormat;

        /// <summary>
        /// Source to display from kinect
        /// </summary>
        [DataMember(Name = "Source")]
        private KinectVisualizationSourceEnum source;

        /// <summary>
        /// Minimum masking range
        /// </summary>
        [DataMember(Name = "MinDepthMaskingRange")]
        private short minDepthMaskingRange;

        /// <summary>
        /// Maximum masking range
        /// </summary>
        [DataMember(Name = "MaxDepthMaskingRange")]
        private short maxDepthMaskingRange;

        /// <summary>
        /// Initializes a new instance of the DepthIRColorCameraModelAgent class
        /// </summary>
        /// <param name="name">Agent name</param>
        /// <param name="producer">The name of the agent we subscribe to.</param>
        /// <param name="maxUpdateIntervalMilliseconds">The interval on which to issue data binding updates to the UI</param>
        /// <param name="disparityFactor">Factor used to accentuate the difference between nearby pixels.</param>
        /// <param name="imageFormat">The image format to use.</param>
        /// <param name="source">Source image from kinect message</param>
        /// <param name="minMask"> Minimum mask</param>
        /// <param name="maxMask"> Maximum mask</param>
        public DepthIRColorCameraModelAgent(
            string name,
            IProducer<DepthIRColorCameraMessage<TDepthParameters, TIRParameters, TColorParameters>> producer,
            int maxUpdateIntervalMilliseconds,
            int disparityFactor,
            SimplePixelFormat imageFormat,
            KinectVisualizationSourceEnum source = KinectVisualizationSourceEnum.DepthImageSource,
            short minMask = 0,
            short maxMask = 10000)
            : base(name, producer.Name, maxUpdateIntervalMilliseconds)
        {
            this.disparityFactor = disparityFactor;
            this.imageFormat = imageFormat;
            this.source = source;
            this.minDepthMaskingRange = minMask;
            this.maxDepthMaskingRange = maxMask;
        }

        /// <summary>
        /// Enumeration of visualization data source
        /// </summary>
        public enum KinectVisualizationSourceEnum
        {
            /// <summary>
            /// Source is depth Image
            /// </summary>
            [EnumMemberAttribute]
            DepthImageSource,

            /// <summary>
            /// Infra red image source
            /// </summary>
            [EnumMemberAttribute]
            IRimageSource,

            /// <summary>
            /// the RGB image source
            /// </summary>
            [EnumMemberAttribute]
            ColorImageSource,
        }

        /// <summary>
        /// Initializes any fields not provided in the manifest.
        /// </summary>
        /// <param name="locator">Not used.</param>
        public override void Initialize(AgentLocator locator)
        {
            base.Initialize(locator);

            if (this.disparityFactor == 0)
            {
                this.disparityFactor = 1;
            }

            if (this.imageFormat == default(SimplePixelFormat))
            {
                this.imageFormat = SimplePixelFormat.Gray16;
            }
        }

        /// <summary>
        /// Updates the model data with the new state.
        /// </summary>
        /// <param name="state">The new state from the upstream agent.</param>
        /// <returns>true if the model was updated, false otherwise</returns>
        protected override bool UpdateModel(DepthIRColorCameraMessage<TDepthParameters, TIRParameters, TColorParameters> state)
        {
            System.Drawing.Size size = new System.Drawing.Size(state.DepthFrame.Width, state.DepthFrame.Height);

            if (this.imageFormat == SimplePixelFormat.Rgb24)
            {
                byte[] image = null;
                int stride = state.DepthFrame.Width * 3;
                DepthCameraAgentMessage<KinectDepthCameraParameters> kinectFrame;

                switch (this.source)
                {
                    case KinectVisualizationSourceEnum.DepthImageSource:
                        kinectFrame = new DepthCameraAgentMessage<KinectDepthCameraParameters>(
                                    state.OriginatingTime,
                                    state.DepthFrame,
                                    new KinectDepthCameraParameters(state.DepthCamera.Parameters));
                        image = DepthImageVisualizationUtilities.Prepare24BitRGBDepthImageFromKinectAgentMessage(kinectFrame, this.disparityFactor);
                        break;

                    case KinectVisualizationSourceEnum.IRimageSource:
                        image = DepthImageVisualizationUtilities.Prepare24BitRGBFromIr(state.IRFrame, this.disparityFactor);
                        break;

                    case KinectVisualizationSourceEnum.ColorImageSource:
                        if (null != state.ColorFrame)
                        {
                            image = state.ColorFrame.GetAsByteArray();
                            size = new System.Drawing.Size(state.ColorFrame.Width, state.ColorFrame.Height);
                            stride = state.ColorFrame.Width * 3;
                        }

                        break;

                    default:
                        kinectFrame = new DepthCameraAgentMessage<KinectDepthCameraParameters>(
                                    state.OriginatingTime,
                                    state.DepthFrame,
                                    new KinectDepthCameraParameters(state.DepthCamera.Parameters));
                        image = DepthImageVisualizationUtilities.Prepare24BitRGBDepthImageFromKinectAgentMessage(kinectFrame, this.disparityFactor);
                        break;
                }

                this.Model.VideoFrame = new VideoFrame()
                {
                    Data = image,
                    Size = size,
                    Format = SimplePixelFormat.Rgb24,
                    Stride = stride
                };
            }
            else
            {
                throw new Exception("DepthCamera - unsupported image type " + this.imageFormat.ToString());
            }

            return true;
        }
    }
}
